package myzzyl.service.Impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import context.JwtUserContext;
import lombok.extern.slf4j.Slf4j;
import myzzyl.constants.CommonIntField;
import myzzyl.constants.CommonStrField;
import myzzyl.constants.ErrorConstants;
import myzzyl.domain.dto.SysResourceDto;
import myzzyl.domain.pojo.JwtUser;
import myzzyl.domain.pojo.SysResource;
import myzzyl.domain.pojo.SysUser;
import myzzyl.domain.vo.*;
import myzzyl.exception.BusinessException;
import myzzyl.service.SysResourceService;
import myzzyl.mapper.SysResourceMapper;
import myzzyl.utils.JwtUtil;
import myzzyl.utils.NoProcessingUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* @author 33249
* @description 针对表【sys_resource(权限表)】的数据库操作Service实现
* @createDate 2025-08-18 17:51:42
*/
@Service
@Slf4j
public class SysResourceServiceImpl extends ServiceImpl<SysResourceMapper, SysResource>
    implements SysResourceService {

    /**
     * 获取资源列表数据
     * @param sysResourceDto 资源表单请求封装类
     * @return 返回值
     */
    @Override
    public List<SysResourceVo> getAll(SysResourceDto sysResourceDto) {
        List<SysResource> sysResourceList = lambdaQuery()
                .eq(StrUtil.isNotBlank(sysResourceDto.getResourceType()), SysResource::getResourceType,
                        sysResourceDto.getResourceType())
                .eq(StrUtil.isNotBlank(sysResourceDto.getParentResourceNo()), SysResource::getParentResourceNo,
                        sysResourceDto.getParentResourceNo())
                .orderByAsc(SysResource::getSortNo)
                .orderByDesc(SysResource::getCreateTime)
                .list();

        return BeanUtil.copyToList(sysResourceList, SysResourceVo.class);
    }

    /**
     * 获取资源详情数据
     * @param resourceNo 资源编号
     * @return 返回值
     */
    @Override
    public SysResourceVo getInfo(String resourceNo) {
        // 查询资源是否存在
        SysResource resource = lambdaQuery()
                .eq(SysResource::getResourceNo, resourceNo)
                .one();
        if(resource == null) {
            throw new BusinessException(ErrorConstants.RESOURCE_IS_NOT_EXIST);
        }

        return BeanUtil.toBean(resource, SysResourceVo.class);
    }


    /**
     * 新增资源
     * @param sysResourceDto 资源表单请求封装类
     * @return 返回值
     */
    @Override
    public boolean add(SysResourceDto sysResourceDto) {
        // 判断资源名称是否为空
        if (StrUtil.isBlank(sysResourceDto.getResourceName())) {
            throw new BusinessException(ErrorConstants.RESOURCE_NAME_IS_NULL);
        }

        // 查询上级菜单是否存在
        if (!lambdaQuery()
                .eq(SysResource::getResourceNo, sysResourceDto.getParentResourceNo())
                .exists()) {
            throw new BusinessException(ErrorConstants.PARENT_RESOURCE_IS_NOT_EXIST);
        }

        // 判断新增是否为菜单且层级超过三层
        //100 001 000 000 000
        //100 001 001 000 000
        //100 001 001 001 000
        //100 001 001 001 001
        if(sysResourceDto.getResourceType().equals(CommonStrField.RESOURCE_TYPE_IS_M.getStr()) &&
                NoProcessingUtil.processString(sysResourceDto.getParentResourceNo()).length() / 3 >= 5){
            throw new BusinessException(ErrorConstants.MENU_RESOURCE_LEVEL_IS_OVER);
        }

        // 最大编号，用于生成当前新增资源的编号
        String maxNo = "";
        // 新增资源的编号
        String newResourceNo = null;

        // 查询上级菜单下的所有子菜单
        List<SysResource> resourceList = lambdaQuery()
                .eq(SysResource::getParentResourceNo, sysResourceDto.getParentResourceNo())
                .list();

        // 循环遍历resourceList，判断是否同名的，且更新最大值
        for (SysResource resource : resourceList) {
            if (resource.getResourceName().equals(sysResourceDto.getResourceName())) {
                throw new BusinessException(ErrorConstants.RESOURCE_IS_ALREADY_EXIST);
            }
            maxNo = resource.getResourceNo().compareTo(maxNo) < 0 ? maxNo : resource.getResourceNo();
        }

        // 判断最大编号是否为空
        if (StrUtil.isBlank(maxNo)) {
            // 为空，说明无下属节点，传递false直接在父级资源编号后+"000"
            newResourceNo = NoProcessingUtil.createNo(sysResourceDto.getParentResourceNo(), false);
        }else {
            // 不为空，传递true，动态拼接
            newResourceNo = NoProcessingUtil.createNo(maxNo, true);
        }

        // 新增资源项目
        SysResource sysResource = new SysResource();
        BeanUtils.copyProperties(sysResourceDto, sysResource);
        sysResource.setResourceNo(newResourceNo);

        return save(sysResource);
    }


    /**
     * 编辑资源
     * @param sysResourceDto 资源表单请求封装类
     * @return 返回值
     */
    @Override
    @Transactional
    public boolean edit(SysResourceDto sysResourceDto) {
        // 判断资源名称是否为空
        if (StrUtil.isBlank(sysResourceDto.getResourceName())) {
            throw new BusinessException(ErrorConstants.RESOURCE_NAME_IS_NULL);
        }

        // 判断修改的层级是不是本身，即parentNo和no相等
        if (sysResourceDto.getParentResourceNo().equals(sysResourceDto.getResourceNo())) {
            throw new BusinessException(ErrorConstants.PARENT_RESOURCE_IS_NOT_SELF);
        }

        // 查询上级资源是否存在
        if (!lambdaQuery()
                .eq(SysResource::getResourceNo, sysResourceDto.getParentResourceNo())
                .exists()) {
            throw new BusinessException(ErrorConstants.PARENT_RESOURCE_IS_NOT_EXIST);
        }

        // 查询要修改的上级资源下的所有子资源（编号从下到大）
        List<SysResource> resourceList = lambdaQuery()
                .eq(SysResource::getParentResourceNo, sysResourceDto.getParentResourceNo())
                .eq(sysResourceDto.getResourceType().equals(CommonStrField.RESOURCE_TYPE_IS_M.getStr()),
                        SysResource::getResourceType, CommonStrField.RESOURCE_TYPE_IS_M.getStr())
                .orderByAsc(SysResource::getResourceNo)
                .list();

        // 循环遍历判断是否有同名的（除去自己）
        resourceList.forEach(resource -> {
            if (!resource.getId().equals(sysResourceDto.getId()) &&
               resource.getResourceName().equals(sysResourceDto.getResourceName())) {
                throw new BusinessException(ErrorConstants.RESOURCE_IS_ALREADY_EXIST);
            }
        });

        // 查询要修改的资源
        SysResource resource = getById(sysResourceDto.getId());
        if (resource == null) {
            throw new BusinessException(ErrorConstants.RESOURCE_IS_NOT_EXIST);
        }

        // 判断上级资源是否发生变化
        if (!resource.getParentResourceNo().equals(sysResourceDto.getParentResourceNo())) {
            // 发生变化，查询当前菜单及其所有子孙节点（编号从小到大）
            List<SysResource> sysResourceList = lambdaQuery()
                    .likeRight(SysResource::getResourceNo, NoProcessingUtil.processString(sysResourceDto.getResourceNo()))
                    .eq(SysResource::getResourceType, CommonStrField.RESOURCE_TYPE_IS_M.getStr())
                    .orderByAsc(SysResource::getResourceNo)
                    .list();

            // 编号最大的层级肯定也是最大的，获取最大层级（减去二的原因是因为根菜单默认的层级计算为2）
            SysResource sysResource = sysResourceList.get(sysResourceList.size() - 1);
            int maxLevel = NoProcessingUtil.processString(sysResource.getResourceNo()).length() / 3 - 2;
            log.info("最大层级：{}", maxLevel);

            // 获取当前层级
            int currentLevel = NoProcessingUtil.processString(resource.getResourceNo()).length() / 3 - 2;
            log.info("当前层级：{}", currentLevel);

            // 获取修改的上级菜单层级
            int parentLevel = NoProcessingUtil.processString(sysResourceDto.getParentResourceNo()).length() / 3 - 2;
            log.info("上级菜单层级：{}", parentLevel);

            // 最大层级 - 当前层级 + 1 + 修改的上级菜单层级，判断是否超出层级上限
            if (sysResourceDto.getResourceType().equals(CommonStrField.RESOURCE_TYPE_IS_M.getStr()) &&
                    maxLevel - currentLevel + 1 + parentLevel > CommonIntField.RESOURCE_LEVEL_MAX_LIMIT.getNumber()) {
                throw new BusinessException(ErrorConstants.MENU_RESOURCE_LEVEL_IS_OVER);
            }

            // 当前菜单旧的编号值，用于递归查询子菜单
            String oldResourceNo = sysResource.getResourceNo();
            // 基于最大编号生成新编号
            String newResourceNo = null;

            // 判断resourceList是否为空
            if (CollectionUtil.isEmpty(resourceList)) {
                // 为空，说明无下属节点，传递false直接在父级资源编号后+"000"
                newResourceNo = NoProcessingUtil.createNo(sysResourceDto.getParentResourceNo(), false);
            }else {
                // 不为空，传递true，动态拼接
                newResourceNo = NoProcessingUtil.createNo(resourceList.get(resourceList.size() - 1).getResourceNo(), true);
            }

            // 先修改当前菜单的编号及其上级菜单编号
            boolean isSuccess = lambdaUpdate()
                    .set(SysResource::getParentResourceNo, sysResourceDto.getParentResourceNo())
                    .set(SysResource::getResourceNo, newResourceNo)
                    .eq(SysResource::getId, sysResourceDto.getId())
                    .update();

            if (isSuccess) {
                // 递归修改菜单编号及其上级菜单编号
                updateResourceNo(oldResourceNo, newResourceNo);
            }

        }

        // 更新资源
        return lambdaUpdate()
                .set(StrUtil.isNotBlank(sysResourceDto.getResourceName()), SysResource::getResourceName, sysResourceDto.getResourceName())
                .set(StrUtil.isNotBlank(sysResourceDto.getRequestPath()), SysResource::getRequestPath, sysResourceDto.getRequestPath())
                .set(sysResourceDto.getSortNo() != null, SysResource::getSortNo, sysResourceDto.getSortNo())
                .eq(SysResource::getId, sysResourceDto.getId())
                .update();
    }


    /**
     * 递归修改菜单编号及其上级菜单编号
     * @param oldParentResourceNo 旧的上级菜单编号
     * @param newParentResourceNo 新的上级菜单编号
     */
    private void updateResourceNo(String oldParentResourceNo, String newParentResourceNo) {
        // 根据旧的上级菜单编号查询子菜单（编号从小到大）
        List<SysResource> resourceList = lambdaQuery()
                .eq(SysResource::getParentResourceNo, oldParentResourceNo)
                .orderByAsc(SysResource::getResourceNo)
                .list();

        // 判断是否为空
        if (CollectionUtil.isEmpty(resourceList)) {
            return;
        }

        // 循环遍历修改
        for (int i = 0; i < resourceList.size(); i++) {
            // 获取当前菜单
            SysResource resource = resourceList.get(i);
            // 旧值
            String oldResourceNo = resource.getResourceNo();
            // 直接构造新值
            String newResourceNo = newParentResourceNo + String.format("%03d", (i + 1));

            // 修改编号
            boolean isSuccess = lambdaUpdate()
                    .set(SysResource::getParentResourceNo, newParentResourceNo)
                    .set(SysResource::getResourceNo, newResourceNo)
                    .eq(SysResource::getId, resource.getId())
                    .update();

            if (!isSuccess) {
                throw new BusinessException(ErrorConstants.UNKNOWN_ERROR);
            }

            // 递归修改
            updateResourceNo(oldResourceNo, newParentResourceNo);
        }

    }


    /**
     * 删除资源
     * @param resourceNo 资源编号
     * @return 返回值
     */
    @Override
    @Transactional
    public boolean remove(String resourceNo) {
        // 查询资源是否存在
        SysResource resource = lambdaQuery()
                .eq(SysResource::getResourceNo, resourceNo)
                .one();
        if (resource == null) {
            throw new BusinessException(ErrorConstants.RESOURCE_IS_NOT_EXIST);
        }

        // 判断资源的状态是否为启用
        if (resource.getDataState().equals("0")) {
            throw new BusinessException(ErrorConstants.RESOURCE_IS_ACTIVE);
        }

        // 判断资源类型是否为菜单
        if (resource.getResourceType().equals(CommonStrField.RESOURCE_TYPE_IS_M.getStr())) {
            // 查询资源是否有子资源
            List<SysResource> resourceList = lambdaQuery()
                    .eq(SysResource::getParentResourceNo, resourceNo)
                    .eq(SysResource::getResourceType, CommonStrField.RESOURCE_TYPE_IS_M.getStr())
                    .list();
            if (CollectionUtil.isNotEmpty(resourceList)) {
                throw new BusinessException(ErrorConstants.RESOURCE_HAS_SUB_RESOURCE);
            }

            // 删除按钮资源
            baseMapper.deleteByParentSource(resourceNo);
        }

        // 删除资源
        return baseMapper.deleteById(resource.getId()) > 0;
    }


    /**
     * 启用或禁用资源
     * @param sysResourceDto 资源表单请求封装类
     * @return 返回值
     */
    @Override
    @Transactional
    public boolean enableOrDisableSysResource(SysResourceDto sysResourceDto) {
        // 查询资源是否存在
        SysResource resource = lambdaQuery()
                .eq(SysResource::getResourceNo, sysResourceDto.getResourceNo())
                .one();
        if (resource == null) {
            throw new BusinessException(ErrorConstants.RESOURCE_IS_NOT_EXIST);
        }

        // 判断资源类型是否为菜单
        if (resource.getResourceType().equals(CommonStrField.RESOURCE_TYPE_IS_M.getStr())) {
            // 判断要修改的资源状态是否为启用
            if (sysResourceDto.getDataState().equals("0")) {
                // 查询父级资源是否存在
                SysResource parentResource = lambdaQuery()
                        .eq(SysResource::getResourceNo, sysResourceDto.getParentResourceNo())
                        .one();
                if (parentResource == null) {
                    throw new BusinessException(ErrorConstants.PARENT_RESOURCE_IS_NOT_EXIST);
                }

                // 判断资源状态是否为禁用
                if (parentResource.getDataState().equals("1")) {
                    throw new BusinessException(ErrorConstants.PARENT_RESOURCE_IS_DISABLE);
                }
            }else {
                // 查询所有子孙资源的资源编号
                List<SysResource> resourceList = lambdaQuery()
                        .likeRight(SysResource::getResourceNo,
                                NoProcessingUtil.processString(sysResourceDto.getResourceNo()))
                        .eq(SysResource::getResourceType, CommonStrField.RESOURCE_TYPE_IS_M.getStr())
                        .list();

                // 判断是否不为空
                if (CollectionUtil.isNotEmpty(resourceList)) {
                    // 获取对应的编号集合
                    List<String> resourceNos = resourceList.stream().map(SysResource::getResourceNo).toList();

                    // 修改状态为禁用
                    lambdaUpdate()
                            .set(SysResource::getDataState, "1")
                            .in(SysResource::getResourceNo, resourceNos)
                            .update();
                }

            }
        }

        // 修改当前资源状态
        return lambdaUpdate()
                .set(SysResource::getDataState, sysResourceDto.getDataState())
                .eq(SysResource::getResourceNo, sysResourceDto.getResourceNo())
                .update();
    }


    /**
     * 获取树形资源
     * @param sysResourceDto 资源表单请求封装类
     * @return 返回值
     */
    @Override
    public TreeVo tree(SysResourceDto sysResourceDto) {
        // 查询所有资源（编号从小到大）
        List<SysResource> resourceList = lambdaQuery()
                .orderByAsc(SysResource::getResourceNo)
                .list();

        // 构造Vo对象返回
        TreeItemVo treeItemVo = createTree(resourceList.get(0), resourceList);
        return new TreeVo(List.of(treeItemVo));
    }


    /**
     * 构建树形资源
     * @param sysResource 当前资源
     * @param resourceList 所有资源集合
     * @return 返回值
     */
    private TreeItemVo createTree(SysResource sysResource, List<SysResource> resourceList) {
        // 构造TreeItemVo对象
        TreeItemVo treeItemVo = new TreeItemVo();
        treeItemVo.setId(sysResource.getResourceNo());
        treeItemVo.setLabel(sysResource.getResourceName());

        // 查询当前资源的所有子资源
        List<SysResource> resources = resourceList
                .stream()
                .filter(resource ->
                        resource.getParentResourceNo().equals(sysResource.getResourceNo()))
                .toList();

        // 判断是否为空
        if (CollectionUtil.isEmpty(resources)) {
            treeItemVo.setChildren(null);
        }else {
            // 当前资源所有子资源构造的TreeItemVo
            List<TreeItemVo> treeItemVoList = new ArrayList<>();

            for (SysResource resource : resources) {
                // 递归获取构造好的TreeItemVo
                TreeItemVo itemVo = createTree(resource, resourceList);
                treeItemVoList.add(itemVo);
            }

            // 设置children属性返回
            treeItemVo.setChildren(treeItemVoList);
        }

        return treeItemVo;
    }


    /**
     * 获取资源菜单
     * @return 返回值
     */
    @Override
    public List<MenuVo> getMenus() {
        //获取当前用户id
//        JwtUser user = JwtUserContext.getJwtUser();
        JwtUser user = (JwtUser) StpUtil.getSession().get("jwtUser");
        if (user == null) {
            throw new BusinessException(ErrorConstants.USER_IS_NOT_LOGIN);
        }

        // 根据用户id查询用户绑定的菜单资源
        List<MenuVo> menuVoList = baseMapper.selectMenuResourcesByUserId(user.getId());

        // 判断菜单资源是否为空
        if (CollectionUtil.isEmpty(menuVoList)) {
            throw new BusinessException(ErrorConstants.USER_NOT_HAS_MENU_POWER);
        }

        // 根据资源编号进行分组
        Map<String, List<MenuVo>> parentNoMap = menuVoList
                .stream()
                .collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        // 构造树形结构返回
        menuVoList.forEach(menuVo -> {
            menuVo.setMeta(new MenuMetaVo(menuVo.getName(), null, null));
            menuVo.setRedirect("/" + menuVo.getName());

            // 根据父资源编号从map集合中获取对应的子资源
            List<MenuVo> subMenuVos = parentNoMap.get(menuVo.getResourceNo());
            // 判断是否为不为空
            if (CollectionUtil.isNotEmpty(subMenuVos)) {
                subMenuVos.forEach(subMenuVo -> {
                    subMenuVo.setMeta(new MenuMetaVo(subMenuVo.getName(), null, null));
                    subMenuVo.setRedirect("/" + subMenuVo.getName());
                });
                menuVo.setChildren(subMenuVos);
            }
        });

        return parentNoMap.get(CommonStrField.ROOT_RESOURCE_NO.getStr());
    }


    /**
     * 根据用户id查询用户绑定的按钮资源
     * @param id 用户id
     * @return 返回值
     */
    @Override
    public List<String> selectButtonResourcesByUserId(Long id) {
        return baseMapper.selectButtonResourcesByUserId(id);
    }

}
